/*******************************************************************************
 * Classe Microphone : classe de gestion du matériel sonore (capture de son,
 *                     diffusion de son)
 ******************************************************************************/
package ElementsDeJeu;

import java.io.*;
import javax.sound.sampled.*;
import prjintwg.PnlJeu;
import sun.audio.*;

/**
 *
 * @author W&G (Code repris et adapté de :
 * http://www.developer.com/java/other/article.php/1579071/Java-Sound-Getting-Started-Part-2-Capture-Using-Specified-Mixer.htm
 */
public class Microphone {

    private boolean stopCapture = false;//état d'arret de la capture de son
    //Flux de sortie
    private ByteArrayOutputStream byteArrayOutputStream;
    //Format audio
    private AudioFormat audioFormat = new AudioFormat(8000.0F, 16, 1, true, false);
    //Cible des donnée de son
    private TargetDataLine targetDataLine;
    //Flux audio entrant
    private AudioInputStream audioInputStream;
    private SourceDataLine sourceDataLine;//Source de la ligne audio
    private Thread playThread;//Processus de lancement du son
    private Thread captureThread;//Processus de capture du son
    private int peakLevel = -255;//Niveau maximum
    private PnlJeu pnlJeu;//Panneau du jeu
    private int cri = 0; // Détermine si c'est la bombe sonore ou le cri de guerre

    //   private Line linetest;
    /**
     * Constructeur du microphone
     * @param pnlJeu panneau du jeu
     * @param cri détermine si la provenance de l'instance est un cri de guerre
     */
    public Microphone(PnlJeu pnlJeu, int cri) {
        this.pnlJeu = pnlJeu;
        this.cri = cri;
    }

    /**
     * Methode de capture du son
     */
    public void captureAudio() {
        try {
            DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class,
                    audioFormat);
            targetDataLine = (TargetDataLine) AudioSystem.getLine(dataLineInfo);
            targetDataLine.open(audioFormat);
            targetDataLine.start();

            captureThread = new Thread(new CaptureThread());
            captureThread.start();
            while (captureThread.isAlive()) {
            }
            targetDataLine.close();
        } catch (Exception e) {
            System.out.println(e);
        }
    }

    /**
     * Retourne le processus de capture du son
     * @return Retourne le processus de capture du son
     */
    public Thread getCaptureThread() {
        return captureThread;
    }

    /**
     * Retourne le processus de lancement du son
     * @return Retourne le processus de lancement du son
     */
    public Thread getPlayThread() {
        return playThread;
    }

    /**
     * Joue un fichier son
     */
    public void playAudio() {
        try {
            byte audioData[] = byteArrayOutputStream.toByteArray();

            InputStream byteArrayInputStream = new ByteArrayInputStream(audioData);
            audioInputStream = new AudioInputStream(byteArrayInputStream,
                    audioFormat, audioData.length / audioFormat.getFrameSize());
            DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class,
                    audioFormat);
            sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
            sourceDataLine.open(audioFormat);
            sourceDataLine.start();

            playThread = new Thread(new PlayThread());
            playThread.start();
        } catch (Exception e) {
            System.out.println(e);
        }
    }

    /**
     * Calcule le niveau Rms du son
     * @param audioData Données sonores
     * @return le niveau Rms du son
     */
    protected int calculateRMSLevel(byte[] audioData) {
        long lSum = 0;
        for (int i = 0; i < audioData.length; i++) {
            lSum = lSum + audioData[i];
        }

        double dAvg = lSum / audioData.length;

        double sumMeanSquare = 0d;
        for (int j = 0; j < audioData.length; j++) {
            sumMeanSquare = sumMeanSquare + Math.pow(audioData[j] - dAvg, 2d);
        }

        double averageMeanSquare = sumMeanSquare / audioData.length;
        return (int) ((Math.pow(averageMeanSquare, 0.5d) - 20.5));
    }

    /**
     * Retourne le volume maximal
     * @return Retourne le volume maximal
     */
    public int getVolume() {
        return peakLevel;
    }

    /**
     * Processus de capture du son
     */
    class CaptureThread extends Thread {

        byte tempBuffer[] = new byte[1000];

        @Override
        public void run() {
            peakLevel = -255;
            byteArrayOutputStream =
                    new ByteArrayOutputStream();
            stopCapture = false;
            try {
                for (int i = 0; i < 30; i++) {
                    int cnt = targetDataLine.read(tempBuffer, 0, tempBuffer.length);
                    if (cnt > 0) {
                        byteArrayOutputStream.write(tempBuffer, 0, cnt);
                    }
                    peakLevel = Math.max(peakLevel, calculateRMSLevel(tempBuffer));
                    pnlJeu.dessinerVolume(peakLevel, cri);
                }
                byteArrayOutputStream.close();
            } catch (Exception e) {
                System.out.println(e);
            }
        }
    }

    /**
     * Joue un fichier son
     * @param filename nom du fichier a jouer
     */
    public void playSound(String filename) {
        try {
            InputStream in = new FileInputStream(filename);
            AudioStream as = new AudioStream(in);
            AudioPlayer.player.start(as);
        } catch (Exception e) {
            System.out.println(e);
        }
    }

    /**
     * Processus jouant le son
     */
    class PlayThread extends Thread {

        byte tempBuffer[] = new byte[1000];

        @Override
        public void run() {
            try {
                int cnt;
                while ((cnt = audioInputStream.read(tempBuffer, 0,
                        tempBuffer.length)) != -1) {
                    if (cnt > 0) {
                        sourceDataLine.write(tempBuffer, 0, cnt);
                    }
                }
                sourceDataLine.drain();
                sourceDataLine.close();
            } catch (Exception e) {
                System.out.println(e);
            }
        }
    }
}
